package com.iwithlong.filter.http;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created by liyd on 16/5/10.
 */
public class HttpClientHandler implements HttpHandler, Closeable {

    private static final Logger LOG = LoggerFactory.getLogger(HttpClientHandler.class);

    private static final String ENCODE = "UTF-8";

    private static final int TIME_OUT = 300000;

    private static final int CONNECTION_MAX_TOTAL = 100;

    private static final int CONNECTION_MAX_PER_ROUTE = 20;

    /**
     * 此客户端需要一直存在,所以不关闭
     */
    protected CloseableHttpClient httpClient;

    protected BasicCookieStore clientCookieStore;

    public HttpClientHandler() {
        RequestConfig defaultRequestConfig = RequestConfig.custom().setSocketTimeout(TIME_OUT)
                .setConnectTimeout(TIME_OUT).setConnectionRequestTimeout(TIME_OUT).build();

        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(CONNECTION_MAX_TOTAL);
        connectionManager.setDefaultMaxPerRoute(CONNECTION_MAX_PER_ROUTE);

//        cookieStore = new BasicCookieStore();
//        this.httpClient = HttpClientBuilder.create().setDefaultRequestConfig(defaultRequestConfig).setDefaultCookieStore(cookieStore).disableRedirectHandling()
//                .setConnectionManager(connectionManager).setRedirectStrategy(new LaxRedirectStrategy()).build();

        clientCookieStore = new BasicCookieStore();
        this.httpClient = HttpClientBuilder.create().setDefaultRequestConfig(defaultRequestConfig).setDefaultCookieStore(clientCookieStore).disableRedirectHandling()
                .setConnectionManager(connectionManager).setRedirectStrategy(new LaxRedirectStrategy()).build();
    }

    /**
     * 构建HttpPost
     *
     * @param url         the url
     * @param httpRequest the http request
     * @param requestInfo the request info
     * @return http entity enclosing request base
     */
    protected HttpRequestBase buildHttpRequest(String url, HttpRequestBase httpRequest, RequestInfo requestInfo) {
        try {
            httpRequest.setURI(URI.create(url));

//            测试代码,指向抓包软件
//            HttpHost httpHost = new HttpHost("localhost",8888);
//            RequestConfig requestConfig = RequestConfig.custom().setProxy(httpHost).build();
//            httpRequest.setConfig(requestConfig);

            if (requestInfo.getHeaders() != null) {
                List<Header> headers = new ArrayList<>();
                for (Map.Entry<String, String> entry : requestInfo.getHeaders().entrySet()) {
                    if (StringUtils.equalsIgnoreCase("Content-Length", entry.getKey()) || StringUtils.equalsIgnoreCase("Connection", entry.getKey()) || StringUtils.equalsIgnoreCase("Transfer-encoding", entry.getKey()) || StringUtils.equalsIgnoreCase("cookie",entry.getKey())) {
                        continue;
                    }
                    Header header = new BasicHeader(entry.getKey(), entry.getValue());
                    headers.add(header);
                }
                httpRequest.setHeaders(headers.toArray(new Header[]{}));
            }

            if (requestInfo.getParams() != null) {
                List<NameValuePair> nvps = new ArrayList<>();
                for (Map.Entry<String, String> entry : requestInfo.getParams().entrySet()) {
                    nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
                if (httpRequest instanceof HttpEntityEnclosingRequestBase) {
                    ((HttpEntityEnclosingRequestBase) httpRequest).setEntity(new UrlEncodedFormEntity(nvps, ENCODE));
                }
            }
//
//            List<Cookie> oldCookie = cookieStore.getCookies();
//            if (oldCookie != null) {
//                LOG.info("================原cookie==========================");
//                for (Cookie cookie : oldCookie) {
//                    LOG.info(cookie.getName() + " : " + cookie.getValue());
//                }
//            }
//
//            cookieStore.clear();
//            Map<String, String> cookies = requestInfo.getCookies();
//            if (cookies != null) {
//                int begin = StringUtils.indexOf(url, "//") + 2;
//                int end = StringUtils.indexOf(url, "/", begin);
//                String domain = StringUtils.substring(url, begin, end);
//                if (StringUtils.indexOf(domain, ":") != -1) {
//                    domain = StringUtils.split(domain, ":")[0];
//                }
//                LOG.info("================手机端cookie==========================");
//                for (Map.Entry<String, String> entry : cookies.entrySet()) {
//                    LOG.info(entry.getKey() + " : " + entry.getValue());
//                    BasicClientCookie cookie = new BasicClientCookie(entry.getKey(), entry.getValue());
//                    cookie.setDomain(domain);
//                    cookie.setPath("/");
//                    cookie.setAttribute(entry.getKey(), entry.getValue());
//                    cookieStore.addCookie(cookie);
//                }
//            }

            return httpRequest;
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("不支持的编码:" + ENCODE, e);
        }
    }

    /**
     * 处理结果信息
     *
     * @param response
     * @return
     */
    protected ResponseInfo processResponse(CloseableHttpResponse response) {

        try {
            ResponseInfo responseInfo = new ResponseInfo();
            responseInfo.setStatusCode(response.getStatusLine().getStatusCode());
            HttpEntity entity = response.getEntity();
            if (entity.getContentType() != null) {
                responseInfo.setContentType(entity.getContentType().getValue());
            }
            responseInfo.setHeaders(response.getAllHeaders());
            if (entity != null) {
                responseInfo.setBody(EntityUtils.toByteArray(entity));
            }
            // 销毁
            EntityUtils.consume(entity);

            List<Cookie> cookies = this.clientCookieStore.getCookies();
            responseInfo.setCookies(cookies);
            this.clientCookieStore.clear();

            return responseInfo;
        } catch (IOException e) {
            throw new RuntimeException("获取response结果失败", e);
        } finally {
            closeResponse(response);
        }

    }

    public ResponseInfo postMultipart(String postUrl, RequestInfo requestInfo, String fileName,
                                      InputStream fileInputStream) {

        try {
            HttpRequestBase httpPost = buildHttpRequest(postUrl, new HttpPost(), requestInfo);

            InputStreamBody bin = new InputStreamBody(fileInputStream, fileName);
            StringBody uploadFileName = new StringBody(fileName, ContentType.create("text/plain", Consts.UTF_8));

            // 以浏览器兼容模式运行，防止文件名乱码。
            HttpEntity reqEntity = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
                    .addPart("file", bin).addPart("filename", uploadFileName).setCharset(Consts.UTF_8).build();

            ((HttpPost) httpPost).setEntity(reqEntity);

            // 发起请求 并返回请求的响应
            CloseableHttpResponse response = this.httpClient.execute(httpPost);

            return processResponse(response);
        } catch (IOException ex) {
            throw new RuntimeException("发起postMultipart请求失败", ex);
        }
    }

    public ResponseInfo post(String postUrl, RequestInfo requestInfo) {
//        HttpRequestBase httpPost = buildHttpRequest(postUrl, new HttpPost(), requestInfo);

        try {
//            HttpContext httpContext = this.buildCookeContext(postUrl, requestInfo);
            HttpPost httpPost = new HttpPost(postUrl);
//            CloseableHttpResponse response = this.httpClient.execute(httpPost, httpContext);
            CloseableHttpResponse response = this.httpClient.execute(httpPost);
//            BasicCookieStore basicCookieStore = (BasicCookieStore)httpContext.getAttribute(HttpClientContext.COOKIE_STORE);
//            List<Cookie> cookies = basicCookieStore.getCookies();
            return processResponse(response);
        } catch (IOException e) {
            throw new RuntimeException("发起post请求失败", e);
        }
    }


    public ResponseInfo get(String getUrl, RequestInfo requestInfo) {
        try {
//            HttpRequestBase httpGet = buildHttpRequest(getUrl, new HttpGet(), requestInfo);
//            HttpContext httpContext = this.buildCookeContext(getUrl, requestInfo);
//            CloseableHttpResponse response = this.httpClient.execute(httpGet, httpContext);
            HttpGet httpGet = new HttpGet(getUrl);
            CloseableHttpResponse response = this.httpClient.execute(httpGet);
            return processResponse(response);
        } catch (IOException e) {
            throw new RuntimeException("发起get请求失败", e);
        }

    }


    /**
     * 构建cookie对象
     *
     * @param url
     * @param requestInfo
     * @return
     */
    private HttpContext buildCookeContext(String url, RequestInfo requestInfo) {
        HttpContext httpContext = new BasicHttpContext();
        Map<String, String> cookies = requestInfo.getCookies();
        if (cookies != null) {
            BasicCookieStore cookieStore = new BasicCookieStore();
            int begin = StringUtils.indexOf(url, "//") + 2;
            int end = StringUtils.indexOf(url, "/", begin);
            String domain = StringUtils.substring(url, begin, end);
            if (StringUtils.indexOf(domain, ":") != -1) {
                domain = StringUtils.split(domain, ":")[0];
            }
            LOG.info("================手机端cookie==========================");
            for (Map.Entry<String, String> entry : cookies.entrySet()) {
                LOG.info(entry.getKey() + " : " + entry.getValue());
                BasicClientCookie cookie = new BasicClientCookie(entry.getKey(), entry.getValue());
                cookie.setDomain(domain);
                cookie.setPath("/");
//                cookie.setAttribute(entry.getKey(), entry.getValue());
                cookieStore.addCookie(cookie);
            }
            httpContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
        }
        return httpContext;
    }

    public void close() throws IOException {
        httpClient.close();
    }

    private void closeResponse(CloseableHttpResponse response) {
        try {
            response.close();
        } catch (IOException e) {
            //ignore
        }
    }
}
